import { Context, Schema, h, Session } from 'koishi'

export const name = 'tabledrop-dice'

export interface Config {

  commandNamePrefix: string

  enableReplySessionMsg: boolean

  punishList: string[]
  autoCreateNewRoom: boolean
  autoStartNextRound: boolean
  allowLateJoin: boolean

}

export const Config: Schema<Config> = Schema.intersect([

  Schema.object({
    commandNamePrefix: Schema.string()
      .default('骰子')
      .description('指令名前缀'),
  }).description('指令设置'),

  Schema.object({
    enableReplySessionMsg: Schema.boolean()
      .default(true)
      .description('是否启用回复消息中的会话信息'),
  }).description("消息设置"),

  Schema.object({
    punishList: Schema.array(String)
      .default([
        '喝一杯！',
        '唱一首歌！',
        '发自拍！',
        '模仿动物叫！',
        '给群主磕头！',
        '换群昵称一分钟！',
        '发红包！',
      ])
      .role('table')
      .description('惩罚内容列表（可自定义）'),
    autoCreateNewRoom: Schema.boolean()
      .default(true)
      .description('游戏结束后自动重新创建房间（不解散）。'),
    autoStartNextRound: Schema.boolean()
      .default(false)
      .description('自动创建新房间后，是否立即开始新一轮游戏。'),
    allowLateJoin: Schema.boolean()
      .default(true)
      .description('自动模式下，是否允许玩家中途加入并参与下局游戏。')
  }).description("游戏设置")
])

type RoomState = {
  owner: string
  players: Map<string, { name: string, roll1?: number, roll2?: [number, number] }>
  waitingPlayers: Map<string, { name: string }>
  round: 'waiting' | 'rolling1' | 'rolling2' | 'finished'
  challenger?: string
}

const rooms = new Map<string, RoomState>()

function getRoomKey(session: Session) {
  return `${session.platform}:${session.channelId}`
}

// 辅助函数，封装回复逻辑
function sendReply(session: Session, message: string, config: Config) {
  const replyPrefix = config.enableReplySessionMsg ? h.quote(session.messageId) : '';
  return session.send(replyPrefix + message);
}

// 辅助函数，封装新一轮开始的逻辑
async function startNewRound(session: Session, room: RoomState, config: Config, startAutomatically: boolean = false) {
  // 将等待玩家加入到当前玩家列表
  for (const [uid, p] of room.waitingPlayers.entries()) {
    room.players.set(uid, p);
  }
  room.waitingPlayers.clear();

  room.round = 'rolling1';
  for (const p of room.players.values()) {
    delete p.roll1
    delete p.roll2
  };
  room.challenger = undefined;

  let message = `新一轮游戏开始！请房间内玩家依次发送“${config.commandNamePrefix}.投掷”进行第一轮投掷。`;
  if (startAutomatically) {
    message = '本局已结束，自动开启新一轮游戏！\n' + message;
  }
  await sendReply(session, message, config);
}


export function apply(ctx: Context, config: Config) {

  ctx.command(`${config.commandNamePrefix}`, '酒桌骰子游戏')

  ctx.command(`${config.commandNamePrefix}.查看配置`, '查看当前插件配置')
    .action(async ({ session }) => {
      const formattedConfig = JSON.stringify(config, null, 2);
      await sendReply(session, `当前插件配置：\n\n${formattedConfig}\n`, config);
    });

  // 查看房间状态
  ctx.command(`${config.commandNamePrefix}.状态`, '查看当前房间状态')
    .action(async ({ session }) => {
      const key = getRoomKey(session);
      const room = rooms.get(key);
      
      if (!room) {
        await sendReply(session, '本频道没有进行中的房间。', config);
        return;
      }
      
      let statusMessage = `当前房间状态：\n`;
      statusMessage += `房主：${h.at(room.owner)} ${room.players.get(room.owner)?.name}\n`;
      
      const ROUND_STATUS = {
        waiting: '等待中',
        rolling1: '第一轮投掷中',
        rolling2: '挑战者投掷中',
        finished: '已结束',
      }

      statusMessage += `游戏状态：${ROUND_STATUS[room.round] || '未知状态'}\n`;

      // 玩家列表
      statusMessage += `\n**当前参与玩家 (${room.players.size}人)：**\n`;
      if (room.players.size > 0) {
        for (const [uid, player] of room.players.entries()) {
          const user = await session.bot.getUser(uid, session.channelId);
          statusMessage += `- ${h.at(uid)} (${user.name || player.name})\n`;
        }
      } else {
        statusMessage += `- 无\n`;
      }
      
      // 等待玩家列表
      if (config.allowLateJoin) {
        statusMessage += `\n**等待下一轮玩家 (${room.waitingPlayers.size}人)：**\n`;
        if (room.waitingPlayers.size > 0) {
          for (const [uid, player] of room.waitingPlayers.entries()) {
            const user = await session.bot.getUser(uid, session.channelId);
            statusMessage += `- ${h.at(uid)} ${user.name || player.name}\n`;
          }
        } else {
          statusMessage += `- 无\n`;
        }
      }
      
      await sendReply(session, statusMessage, config);
    });

  // 创建房间
  ctx.command(`${config.commandNamePrefix}.创建`, '创建骰子游戏房间')
    .action(async ({ session }) => {
      const key = getRoomKey(session);
      if (rooms.has(key)) {
        await sendReply(session, '本频道已有进行中的房间。', config);
        return;
      }
      const players = new Map<string, { name: string }>();
      const waitingPlayers = new Map<string, { name: string }>();
      players.set(session.userId, { name: session.username });
      rooms.set(key, {
        owner: session.userId,
        players,
        waitingPlayers,
        round: 'waiting',
      });
      await sendReply(session, '房间已创建，等待玩家加入。', config);
    })

  // 加入房间
  ctx.command(`${config.commandNamePrefix}.加入`, '加入骰子游戏房间')
    .action(async ({ session }) => {
      const key = getRoomKey(session);
      const room = rooms.get(key);
      if (!room) {
        await sendReply(session, '本频道没有进行中的房间。', config);
        return;
      }
      if (room.players.has(session.userId) || room.waitingPlayers.has(session.userId)) {
        await sendReply(session, '你已在房间内。', config);
        return;
      }

      if (room.round !== 'waiting' && !config.allowLateJoin) {
        await sendReply(session, '游戏已开始，无法加入。', config);
        return;
      }

      // 如果游戏已开始且允许中途加入
      if (room.round !== 'waiting' && config.allowLateJoin) {
        room.waitingPlayers.set(session.userId, { name: session.username });
        await sendReply(session, '加入成功，你将在下一轮游戏自动参与。', config);
        return;
      }

      // 游戏未开始
      room.players.set(session.userId, { name: session.username });
      await sendReply(session, '加入成功，等待房主开始游戏。', config);
    })

  // 开始游戏
  ctx.command(`${config.commandNamePrefix}.开始`, '开始骰子游戏')
    .action(async ({ session }) => {
      const key = getRoomKey(session);
      const room = rooms.get(key);
      if (!room) {
        await sendReply(session, '本频道没有进行中的房间。', config);
        return;
      }
      if (room.owner !== session.userId) {
        await sendReply(session, '只有房主可以开始游戏。', config);
        return;
      }
      if (room.players.size < 2) {
        await sendReply(session, '至少需要2人参与。', config);
        return;
      }
      
      await startNewRound(session, room, config, false);
    })

  // 投掷骰子
  ctx.command(`${config.commandNamePrefix}.投掷`, '投掷骰子')
    .action(async ({ session }) => {
      const key = getRoomKey(session)
      const room = rooms.get(key)
      if (!room) {
        await sendReply(session, '本频道没有进行中的房间。', config);
        return;
      }
      if (!room.players.has(session.userId)) {
        await sendReply(session, '你不在房间内。', config);
        return;
      }

      // 第一轮
      if (room.round === 'rolling1') {
        const player = room.players.get(session.userId)!
        if (player.roll1 !== undefined) {
          await sendReply(session, '你已投掷过第一轮。', config);
          return;
        }
        const roll = Math.floor(Math.random() * 6) + 1;
        await sendReply(session, `你投掷出了${roll}点。`, config);
        player.roll1 = roll;
        // 检查是否所有人都投完
        const allRolled = Array.from(room.players.values()).every(p => p.roll1 !== undefined)
        if (allRolled) {
          const min = Math.min(...Array.from(room.players.values()).map(p => p.roll1!))
          const challengers = Array.from(room.players.entries()).filter(([_, p]) => p.roll1 === min)
          // 还记得players的定义吗？players: Map<string, { name: string, roll1?: number, roll2?: [number, number] }>
          // room.players.entries() 返回的是一个包含键值对 [key, value] 的迭代器。这里的 key 是 userId，value 是 { name: string, ... }。
          // Array.from(...) 将这个迭代器转换为一个数组。所以 challengers 是一个由 [string, { name: string, ... }] 组成的数组。 
          if (challengers.length > 1) {
            for (const [uid] of challengers) {
              room.players.get(uid)!.roll1 = undefined
            }
            await sendReply(session, `有${challengers.length}人点数最小，请他们重新投掷第一轮。`, config);
            return;
          } else {
            room.challenger = challengers[0][0]
            room.round = 'rolling2'
            const challengerUser = await session.bot.getUser(challengers[0][0], session.channelId);
            // challengers[0]拿到的是这个唯一的挑战者
            // challengers[0][0]拿到的是这个唯一的挑战者的 userId:string
            // challengers[0][1]拿到的是这个唯一的挑战者的玩家对象{ name: string, ... } 
            await sendReply(session, `挑战者是：${h.at(challengers[0][0])} ${challengerUser.name}，请发送“${config.commandNamePrefix}.投掷”进行第二轮（两次）投掷。`, config);
            return;
          }
        }
        return;
      }

      // 第二轮
      if (room.round === 'rolling2') {
        if (room.challenger !== session.userId) {
          await sendReply(session, '现在只允许挑战者投掷。', config);
          return;
        }
        const player = room.players.get(session.userId)!
        if (player.roll2) {
          await sendReply(session, '你已完成第二轮投掷。', config);
          return;
        }
        const rollA = Math.floor(Math.random() * 6) + 1
        const rollB = Math.floor(Math.random() * 6) + 1
        if (rollA === rollB) {
          await sendReply(session, `你投出了${rollA}和${rollB}，点数相同，请重新投掷。`, config);
          return;
        }
        player.roll2 = [rollA, rollB]
        
        const total = rollA + rollB
        const punish = config.punishList[(total - 2) % config.punishList.length]
        
        await sendReply(session, `你投出了${rollA}和${rollB}，总点数${total}。\n惩罚内容：${punish}`, config);
        room.round = 'finished';
        
        // 根据配置决定是否自动创建新房间并继续
        if (config.autoCreateNewRoom) {
            // 游戏状态重置为等待
            room.round = 'waiting';

            // 清空所有玩家的投掷数据
            for (const p of room.players.values()) {
                delete p.roll1;
                delete p.roll2;
            }
            room.challenger = undefined;

            // 将等待玩家加入到当前玩家列表
            for (const [uid, p] of room.waitingPlayers.entries()) {
              room.players.set(uid, p);
            }
            room.waitingPlayers.clear();
          
            if (config.autoStartNextRound) {
                await sendReply(session, `本局游戏已结束，房间已自动重置，新一轮游戏已自动开始。`, config);
                await startNewRound(session, room, config, true);
            } else {
                await sendReply(session, `本局游戏已结束，房间已自动重置。${h.at(room.owner)}房主你好，新的一局游戏已就绪，等待你发送 “${config.commandNamePrefix}.开始” 指令。`, config);
            }
        } else {
            // 游戏结束，移除房间
            rooms.delete(key)
            await sendReply(session, '本局游戏已结束，房间已自动解散。', config);
        }
        return;
      }

      if (room.round === 'finished') {
        await sendReply(session, '本局已结束，请重新开始。', config);
        return;
      }

      await sendReply(session, '当前状态不允许投掷。', config);
    })

  // 取消/解散房间
  ctx.command(`${config.commandNamePrefix}.解散`, '解散当前房间')
    .action(async ({ session }) => {
      const key = getRoomKey(session)
      if (!rooms.has(key)) {
        await sendReply(session, '本频道没有进行中的房间。', config);
        return;
      }
      rooms.delete(key)
      await sendReply(session, '房间已解散。', config);
    })

}

